/*
 * Amkai Enterprise Solution (AES.) 
 * Copyright 2004-2008 by Amkai(TM), Inc.
 * www.amkai.com
 * 
 * Project: webservice
 * Package: com.amkai.aes.webservice.binary.objecthandler.billing.ledger
 * File: PaymentWriteOffTransferHandler.java
 * Created: 2009.05.18. 11:17:41
 * Author: polaj 
 * 
 */
package com.amkai.aes.webservice.binary.objecthandler.billing.ledger;

import java.math.BigDecimal;
import java.util.Calendar;
import java.util.Date;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.amkai.aes.aescommon.billing.BillingConstants;
import com.amkai.aes.aescommon.billing.CustomTransactionRow;
import com.amkai.aes.aescommon.ledger.exceptions.LedgerException;
import com.amkai.aes.aescommon.ledger.managers.ChargeDebitManager;
import com.amkai.aes.aescommon.ledger.managers.ChargeManager;
import com.amkai.aes.aescommon.ledger.managers.PaymentManager;
import com.amkai.aes.aescommon.ledger.managers.TransferManager;
import com.amkai.aes.aescommon.ledger.managers.WriteOffManager;
import com.amkai.aes.aescommon.service.binary.job.billing.ledger.PWTJob;
import com.amkai.aes.aescommon.service.result.billing.ledger.PWTResult;
import com.amkai.aes.generatedbeans.billinginterval.Batch;
import com.amkai.aes.generatedbeans.billingledger.Charge;
import com.amkai.aes.generatedbeans.billingledger.details.JournalCode;
import com.amkai.aes.generatedbeans.organization.Department;
import com.amkai.aes.generatedbeans.passport.Passport;
import com.amkai.aes.generatedbeans.registration.Patient;
import com.amkai.aes.server.billing.ledger.LedgerManager;
import com.amkai.aes.server.billing.ledger.ServerLedgerEntryLoadManager;
import com.amkai.aes.server.session.ServiceSessionManager;
import com.amkai.aes.webservice.binary.JobObjectHandler;
import com.amkai.common.base.utils.TimePartKind;
import com.amkai.common.base.utils.TimeUtilities;
import com.amkai.common.extensions.hibernate.config.SiteManager;
import com.amkai.common.extensions.hibernate.config.manager.SessionManager;
import com.amkai.rad.designerbase.beans.ChangedKind;

public class PWTHandler implements JobObjectHandler<PWTJob, Department, PWTResult>{

	private static final Log log = LogFactory.getLog(PWTHandler.class);
	private static final String TITLE = "Payments / WriteOffs / Transfers";
	
	protected enum WritoffKind {
		WO1, WO2
	}

	@Override
	public PWTResult process(PWTJob job, Department dep) throws Exception {
		switch (job.getKind()) {
		case INIT:	return initPaymentsWriteOffsTransfers(job, dep);
		case SAVE:	return savePaymentsWriteOffsTransfers(job, dep);
		}
		return null;
	}
	
	private PWTResult initPaymentsWriteOffsTransfers(PWTJob job, Department moduleDepartment) throws Exception {
		PWTResult result = new PWTResult();
		SessionManager sm = SiteManager.Start();
		try {
			Patient patient = (Patient)sm.execute("FROM Patient p " +
					"JOIN FETCH p.person pe " +
					"WHERE p.id=:id").setParameter("id", job.getPatientID()).uniqueResult();
			result.setPatient(patient);
		}
		catch (Exception ex) {
			log.error(TITLE, ex);
			result.setException(ex);
		}
		finally {
			sm.closeSession();
		}
		return result;
	}
	
	private PWTResult savePaymentsWriteOffsTransfers(PWTJob job, Department dep) throws Exception {
		PWTResult result = new PWTResult();
		SessionManager sm = SiteManager.Start();
		try {
			sm.openTransaction();
			
			Passport lastEditor = ServiceSessionManager.getConnection().getRuntimePassport();
			
			Batch batch = job.getDomain().getPwtTemplate().getSelectedBatch();
			
			for (CustomTransactionRow ctr : job.getRows()) {
				
				// reload Charge
				ChargeManager cm = new ChargeManager(sm.get(Charge.class, ctr.getCharge().getId()));
				ServerLedgerEntryLoadManager.getChildEntries(cm.getEntry());

				// ----------------------- PAYMENT
				if (ctr.getPaymentAmt() != null) {
					//PaymentManager pm = (PaymentManager)cm.createNewChild(TransactionKind.PAYMENT, LoginProperties.getPassport());
					PaymentManager pm = new PaymentManager(cm.createPayment(lastEditor));
					pm.setTransactionDate(job.getDomain().getPwtTemplate().getTransactionDate());
					pm.setBatch(batch);
					pm.setAmount(ctr.getPaymentAmt());
					pm.setChargeEntryAdjustment(false);
					pm.setJournalCode(ctr.getAdjusmentDetails().getPaymentJC());
					if (pm.getJournalCode() == null)
						throw new LedgerException("Ledger","Missing Journal Code",pm.getTransactionKind().toString()+" has no Journal Code",null);
					pm.setPaymentType(job.getDomain().getPwtTemplate().getPaymentType());
					pm.setCheckNumber(job.getDomain().getPwtTemplate().getCheckNumber());
					pm.setFreeText(job.getDomain().getPwtTemplate().getRouting());
					pm.setNote(ctr.getAdjusmentDetails().getPaymentNote());
					pm.setPrintOnStatement(ctr.getAdjusmentDetails().isPaymentPrintOnStatement());
					pm.setFromEntry(ctr.getFromTo(),true);
					cm.getCharge().setChangedKind(ChangedKind.CHANGED);
					if (job.isPostProcess())
						pm.getEntry().putTranzientProperty(LedgerManager.RCM_POST_PROCESS, Boolean.TRUE);
					pm.getEntry().putTranzientProperty(BillingConstants.INSERT_SEQUENCE_KEY, 1);
				}
				
				// ------------------------------- WRITEOFF1
				if (ctr.getWoff1Amt() != null) {
					//WriteOffManager wom = (WriteOffManager) cm.createNewChild(TransactionKind.WRITEOFF, owner);
					WriteOffManager wom1 = new WriteOffManager(cm.createWriteOff(lastEditor));
					wom1.setTransactionDate(job.getDomain().getPwtTemplate().getTransactionDate());
					wom1.setBatch(batch);
					//initEntryManager(wom);
					wom1.setAmount(ctr.getWoff1Amt());
					wom1.setJournalCode(ctr.getAdjusmentDetails().getWriteoff1JC());
					if (wom1.getJournalCode() == null)
						throw new LedgerException("Ledger","Missing Journal Code",wom1.getTransactionKind().toString()+"1 has no Journal Code",null);
					wom1.setNote(ctr.getAdjusmentDetails().getWriteoff1Note());
					wom1.setPrintOnStatement(ctr.getAdjusmentDetails().isWriteOff1PrintOnStatement());
					wom1.setFromEntry(ctr.getFromTo(),true);
					wom1.setDenialCodePair(ctr.getWoff1DenialCodePair());
					cm.getCharge().setChangedKind(ChangedKind.CHANGED);
					if (job.isPostProcess())
						cm.getEntry().putTranzientProperty(LedgerManager.RCM_POST_PROCESS, Boolean.TRUE);
					wom1.getEntry().putTranzientProperty(BillingConstants.INSERT_SEQUENCE_KEY, 2);
				}
				
				// ------------------------------- WRITEOFF2
				if (ctr.getWoff2Amt() != null) {
					//WriteOffManager wom = (WriteOffManager) cm.createNewChild(TransactionKind.WRITEOFF, owner);
					WriteOffManager wom2 = new WriteOffManager(cm.createWriteOff(lastEditor));
					wom2.setTransactionDate(job.getDomain().getPwtTemplate().getTransactionDate());
					wom2.setBatch(batch);
					//initEntryManager(wom);
					wom2.setAmount(ctr.getWoff2Amt());
					wom2.setJournalCode(ctr.getAdjusmentDetails().getWriteoff2JC());
					if (wom2.getJournalCode() == null)
						throw new LedgerException("Ledger","Missing Journal Code",wom2.getTransactionKind().toString()+"2 has no Journal Code",null);
					wom2.setNote(ctr.getAdjusmentDetails().getWriteoff2Note());
					wom2.setPrintOnStatement(ctr.getAdjusmentDetails().isWriteOff2PrintOnStatement());
					wom2.setFromEntry(ctr.getFromTo(),true);
					wom2.setDenialCodePair(ctr.getWoff2DenialCodePair());
					cm.getCharge().setChangedKind(ChangedKind.CHANGED);
					if (job.isPostProcess())
						cm.getEntry().putTranzientProperty(LedgerManager.RCM_POST_PROCESS, Boolean.TRUE);
					wom2.getEntry().putTranzientProperty(BillingConstants.INSERT_SEQUENCE_KEY, 3);
				}
				
				// --------------------- DEBIT
				if ((ctr.getAdjusmentDetails().getDebit() != null) && (ctr.getAdjusmentDetails().getDebit().doubleValue() != 0)) {
//					ChargeDebitManager dm = (ChargeDebitManager)cm.createNewChild(TransactionKind.CHARGE_DEBIT, LoginProperties.getPassport());
					ChargeDebitManager cdm = new ChargeDebitManager(cm.createDebit(lastEditor));
					cdm.setTransactionDate(job.getDomain().getPwtTemplate().getTransactionDate());
					cdm.setBatch(batch);
					cdm.setAmount(ctr.getAdjusmentDetails().getDebit());
					cdm.setJournalCode(ctr.getAdjusmentDetails().getDebitJC());
					if (cdm.getJournalCode() == null)
						throw new LedgerException("Ledger","Missing Journal Code",cdm.getTransactionKind().toString()+" has no Journal Code",null);
					cdm.setNote(ctr.getAdjusmentDetails().getDebitNote());
					cdm.setPrintOnStatement(ctr.getAdjusmentDetails().isDebitPrintOnStatement());
					cm.getCharge().setChangedKind(ChangedKind.CHANGED);
					cdm.getEntry().putTranzientProperty(BillingConstants.INSERT_SEQUENCE_KEY, 4);
				}

				// ---------------------------- CHARGE
				if (ctr.getAdjusmentDetails().getPatientResp() != null) {
					cm.setPatientResponsibility(ctr.getAdjusmentDetails().getPatientResp());
				}
//				if (ctr.getAdjusmentDetails().getPatientPaid() != null) {
//					cm.setPatientAmountPaid(ctr.getAdjusmentDetails().getPatientPaid());
//				}
				if (ctr.getAdjusmentDetails().getCopay() != null) {
					cm.setCopay(ctr.getAdjusmentDetails().getCopay());
				}
				cm.setOriginalClaimReferenceNumber(ctr.getAdjusmentDetails().getOriginalClaimReferenceNumber());
				cm.setResubmissionCode(ctr.getAdjusmentDetails().getResubmissionCode());
				cm.setAdjudicationDate(ctr.getAdjusmentDetails().getAdjudicationDate());
				cm.setPatientResponsibility(ctr.getPatientResponsible());
				cm.setAllowedAmount(ctr.getAllowedAmt());
				cm.setDeductibleAmount(ctr.getDeductibleAmt());
				cm.setCoinsurance(ctr.getCoInsAmt());
				BigDecimal ptPaid = ctr.getAdjusmentDetails().getPatientPaid();
				if (ptPaid == null)
					ptPaid = BigDecimal.ZERO;
				cm.setPatientAmountPaid(ptPaid);
				BigDecimal copay = ctr.getAdjusmentDetails().getCopay();
				if (copay == null)
					copay = BigDecimal.ZERO;
				cm.setCopay(copay);
								
				// --------------------------- TRANSFER
				if ((ctr.getXferAmt() != null) && (ctr.getBillTo().getKind() != null)) {
					if (ctr.isTransfer() || (ctr.isTransferFlag() && job.getDomain().getPwtTemplate().getAutoXfer())) {
						if (ctr.getBillTo().getKind().ordinal() != ctr.getFromTo().getKind().ordinal()) {
							TransferManager tm = new TransferManager(cm.createTransfer(lastEditor));
							tm.setTransactionDate(job.getDomain().getPwtTemplate().getTransactionDate());
							tm.setBatch(batch);
							tm.setToEntry(ctr.getBillTo());
							JournalCode tJC = null;
							switch (tm.getToManager().getKind()) {
							case SECONDARY_INSURANCE:
								tJC = ctr.getAdjusmentDetails().getSecInsJC();
								break;
							case TERTIARY_INSURANCE:
								tJC = ctr.getAdjusmentDetails().getTerInsJC();
								break;
							case PRIMARY_GUARANTOR:
//							case SECONDARY_GUARANTOR:
								tJC = ctr.getAdjusmentDetails().getGarantJC();
								break;
							default:
								tJC = ctr.getAdjusmentDetails().getOtherInsJC();
								break;
							}
							tm.setJournalCode(tJC);

							tm.setNote(ctr.getAdjusmentDetails().getTransferNote());
							tm.setAmount(ctr.getXferAmt());
							tm.setDue(tm.getAmount());
							tm.setFromEntry(ctr.getFromTo());
							tm.getEntry().putTranzientProperty(BillingConstants.INSERT_SEQUENCE_KEY, 5);
							
							cm.setFromByTransfer(tm);
							
							//AES-3513
							//AES-10419 should clear the bill date when transferred to an Insurance
							//if (cm.getCurrentBillCharge() != null && cm.getDue().doubleValue() > 0)
							switch (cm.getFromEntry().getKind()) {
							case PRIMARY_INSURANCE:
							case SECONDARY_INSURANCE:
							case TERTIARY_INSURANCE:
								cm.setCurrentBillCharge(null);
							case PRIMARY_GUARANTOR:
							case SECONDARY_GUARANTOR:
							}
							
							// AES-9164
							// Set the last Transfer Date
							cm.setLastTransferDate(tm.getTransactionDate());
						}
					}
				}
				
				//AES-9460
				cm.setGenerateClaim(ctr.isGenerateClaim());
				//cm.setBalance();
				
				LedgerManager.saveEntry(sm, cm.getCharge());
			}
			
			sm.commitTransaction();
		}
		catch (Exception ex) {
			result.setException(ex);
			sm.rollbackTransaction();
		}
		finally {
			sm.closeSession();
		}
		return result;
	}
}
